package org.vaadin.nf4v.ext.runtime.secure;

import org.vaadin.nf4v.core.NavigableApplication;
import org.vaadin.nf4v.core.navigation.NavigationRequest;
import org.vaadin.nf4v.core.navigation.event.NavigationListener;
import org.vaadin.nf4v.ext.config.secure.ExtSecureUtil;

/**
 * Base class for security listeners.
 * Extend it and implement required security checks.
 * Register resulted listener with PAGE_NAVIGATION state or CONTEXT_ACTIVATED and CONTEXT_NAVIGATION states
 * (or all three).
 */
public abstract class AbstractPageSecurityListener implements NavigationListener {


    @Override
    public void onNavigation(NavigationRequest request) {
        if (!(NavigableApplication.getCurrent() instanceof SecuredApplication)) {
            throw new IllegalStateException("Application isn't security application: " +
                    "to enable application security implement SecuredApplication interface.");
        }
        ISecurityManager manager = ((SecuredApplication) NavigableApplication.getCurrent()).getSecurityManager();
        String[] secure = ExtSecureUtil.getPageRequiredRoles(request);
        if (secure != null && secure.length > 0) {
            if (!manager.isLoggedIn()) {
                cancelRequest(request, null, SecurityViolationType.NOT_LOGGED_IN);
                return;
            }
            for (String role : secure) {
                if (!manager.hasRole(role)) {
                    cancelRequest(request, role, SecurityViolationType.NO_ROLE);
                    return;
                }
            }
        }
        secure = ExtSecureUtil.getPageDeniedRoles(request);
        if (secure != null && secure.length > 0) {
            if (!manager.isLoggedIn()) {
                cancelRequest(request, null, SecurityViolationType.NOT_LOGGED_IN);
                return;
            }
            for (String role : secure) {
                if (manager.hasRole(role)) {
                    cancelRequest(request, role, SecurityViolationType.ROLE_DENIED);
                    return;
                }
            }
        }
        secure = ExtSecureUtil.getPageRequiredPermissions(request);
        if (secure != null && secure.length > 0) {
            if (!manager.isLoggedIn()) {
                cancelRequest(request, null, SecurityViolationType.NOT_LOGGED_IN);
                return;
            }

            for (String permission : secure) {
                if (!manager.hasRole(permission)) {
                    cancelRequest(request, permission, SecurityViolationType.NO_PERMISSION);
                    return;
                }
            }
        }
    }

    protected void cancelRequest(NavigationRequest request, String constraint, SecurityViolationType type){
        request.cancel();
        handleSecurityViolation(request, constraint, type);
    }

    /**
     * Implement access denied logic here.
     * NOTE: Request is already cancelled at this point!
     * <p/>
     * The most obvious implementation is:
     * if user not logged in
     * 1. Store navigation target and redirect to login page:
     * NavigationInterruptor.interruptAndRedirect(request, LoginPage.class)
     * if user logged in
     * <p/>
     * In login page, after successful login continue interrupted navigation
     * (or redirect to home page if login page was called directly):
     * if (!NavigationInterruptor.continueInterrupted()){}
     *    NavigationManager.navigateToHome();
     * }
     *
     * @param request navigation request
     * @param constraint failed constraint (may be null - if user not logged in or role or permission - if constraint check failed)
     * @param type violation type
     */
    protected abstract void handleSecurityViolation(NavigationRequest request, String constraint, SecurityViolationType type);
}
